package edu.se.jai.gameservice.service;

import edu.se.jai.gameservice.entity.GameRoom;
import edu.se.jai.gameservice.entity.Player;
import edu.se.jai.openfeignapi.dto.GameRoomDto;
import edu.se.jai.openfeignapi.dto.GameStatus;
import edu.se.jai.openfeignapi.dto.GuessResultDto;
import edu.se.jai.openfeignapi.dto.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 游戏服务类
 * 
 * 负责管理游戏房间的核心业务逻辑，使用三个核心缓存：
 * - roomIdToRoom: 存储房间ID到房间的映射
 * - sessionIdToPlayer: 存储会话ID到玩家的映射
 * - sessionIdToRoomId: 存储会话ID到所在房间ID的映射
 * 
 * 注意：使用内部实体（Player, GameRoom），包含敏感信息如 targetNumber
 */
@Slf4j
@Service
public class GameService {

    /** 每个房间最大玩家数 */
    @Value("${game.max-players-per-room:8}")
    private int maxPlayersPerRoom;

    /**
     * 存储所有游戏房间（使用内部实体）
     * Key: 房间ID, Value: 游戏房间对象
     */
    private final Map<String, GameRoom> roomIdToRoom = new ConcurrentHashMap<>();

    /**
     * 存储会话ID到玩家的映射（使用内部实体）
     * Key: 会话ID, Value: 玩家对象
     */
    private final Map<String, Player> sessionIdToPlayer = new ConcurrentHashMap<>();

    /**
     * 存储玩家所在的房间
     * Key: 会话ID, Value: 房间ID
     */
    private final Map<String, String> sessionIdToRoomId = new ConcurrentHashMap<>();

    // ==================== 玩家管理方法 ====================
    
    /**
     * 注册玩家会话
     * 
     * @param sessionId 会话ID
     * @param playerName 玩家名称
     * @return 注册结果
     */
    public Result<String> registerPlayerSession(String sessionId, String playerName) {
        // 检查玩家名是否重复
        boolean nameExists = sessionIdToPlayer.values().stream()
                .anyMatch(player -> player.getName().equals(playerName));

        if (nameExists) {
            return Result.failure("玩家名称已存在，请选择其他名称");
        }

        // 创建玩家并注册会话
        Player player = createPlayer(playerName, sessionId);
        sessionIdToPlayer.put(sessionId, player);

        log.info("玩家 {} 会话注册成功，Session ID: {}", playerName, sessionId);
        return Result.success(playerName);
    }

    /**
     * 根据会话ID获取玩家信息
     * 
     * @param sessionId 会话ID
     * @return 玩家对象，如果不存在则返回null
     */
    public Player getPlayerBySessionId(String sessionId) {
        return sessionIdToPlayer.get(sessionId);
    }

    // ==================== 房间管理方法 ====================
    
    /**
     * 创建新房间
     * 
     * @param sessionId 创建者的会话ID
     * @return 创建结果
     */
    public Result<GameRoom> createRoom(String sessionId) {
        // 检查玩家是否已经加入了其他房间
        if (sessionIdToRoomId.containsKey(sessionId)) {
            return Result.failure("您已经加入了其他房间，不能创建新房间");
        }

        // 创建房间对象
        String roomId = generateRoomId();
        GameRoom room = new GameRoom();
        room.setRoomId(roomId);
        room.setCreatorSessionId(sessionId);
        room.setStatus(GameStatus.WAITING);
        room.setTargetNumber(generateTargetNumber());

        // 创建者自动加入房间
        Player creator = sessionIdToPlayer.get(sessionId);
        if (creator == null) {
            return Result.failure("玩家信息不存在");
        }
        
        room.getPlayers().put(sessionId, creator);
        room.setCreatorName(creator.getName());
        roomIdToRoom.put(roomId, room);
        sessionIdToRoomId.put(sessionId, roomId);

        log.info("创建新房间: {}，创建者: {}", roomId, creator.getName());
        return Result.success(room);
    }

    /**
     * 加入房间
     * 
     * @param sessionId 玩家会话ID
     * @param roomId 房间ID
     * @return 加入结果
     */
    public Result<GameRoom> joinRoom(String sessionId, String roomId) {
        // 检查玩家是否已经加入了其他房间
        if (sessionIdToRoomId.containsKey(sessionId)) {
            return Result.failure("您已经加入了其他房间，不能加入更多房间");
        }
        
        // 获取房间信息
        GameRoom room = roomIdToRoom.get(roomId);
        if (room == null) {
            return Result.failure("房间不存在");
        }

        // 获取玩家信息
        Player player = sessionIdToPlayer.get(sessionId);
        if (player == null) {
            return Result.failure("玩家信息不存在");
        }

        // 检查房间是否已满
        if (room.getPlayers().size() >= maxPlayersPerRoom) {
            return Result.failure("房间已满");
        }
        
        // 检查房间状态，只有WAITING状态才能加入
        if (room.getStatus() != GameStatus.WAITING) {
            return Result.failure("游戏已开始，无法加入");
        }

        // 将玩家加入房间
        room.getPlayers().put(sessionId, player);
        sessionIdToRoomId.put(sessionId, roomId);

        log.info("玩家 {} 加入房间 {}", player.getName(), roomId);
        return Result.success(room);
    }

    /**
     * 离开房间
     * 
     * @param sessionId 玩家会话ID
     * @param roomId 房间ID
     * @return 离开结果
     */
    public Result<GameRoom> leaveRoom(String sessionId, String roomId) {
        // 1. 根据roomId获取房间对象
        GameRoom room = roomIdToRoom.get(roomId);
        if (room == null) {
            return Result.failure("房间不存在");
        }

        // 2. 验证玩家是否为房间创建者
        if (room.getCreatorSessionId().equals(sessionId)) {
            return Result.failure("房间创建者不能离开房间");
        }

        // 3.从房间中移除玩家
        Player player = room.getPlayers().remove(sessionId);

        // 4. 清理玩家的sessionIdToRoomId映射
        sessionIdToRoomId.remove(sessionId);

        log.info("玩家 {} 离开房间 {}", player.getName(), roomId);

        return Result.success(room);
    }

    /**
     * 解散房间
     * 
     * @param sessionId 解散者的会话ID
     * @param roomId 房间ID
     * @return 解散结果
     */
    public Result<GameRoom> dissolveRoom(String sessionId, String roomId) {
        // 1. 根据roomId获取房间对象
        GameRoom room = roomIdToRoom.get(roomId);
        if (room == null) {
            return Result.failure("房间不存在");
        }

        // 2. 验证玩家是否为房间创建者
        if (!room.getCreatorSessionId().equals(sessionId)) {
            return Result.failure("只有房间创建者才能解散房间");
        }

        // 3. 清理所有房间内玩家的sessionIdToRoomId映射
        for (String playerSessionId : room.getPlayers().keySet()) {
            sessionIdToRoomId.remove(playerSessionId);
        }

        // 4. 从roomIdToRoom中移除房间
        roomIdToRoom.remove(roomId);

        log.info("房间 {} 已被创建者解散，移除了 {} 个玩家的房间映射", roomId, room.getPlayers().size());

        return Result.success(room);
    }

    /**
     * 获取所有房间列表
     * 
     * @return 所有房间列表
     */
    public List<GameRoom> getAllRooms() {
        return new ArrayList<>(roomIdToRoom.values());
    }

    /**
     * 根据会话ID获取所在房间
     * 
     * @param sessionId 会话ID
     * @return 房间对象，如果不存在则返回null
     */
    public GameRoom getRoomBySessionId(String sessionId) {
        String roomId = sessionIdToRoomId.get(sessionId);
        if (roomId != null) {
            return roomIdToRoom.get(roomId);
        }
        return null;
    }

    /**
     * 根据房间ID获取房间
     * 
     * @param roomId 房间ID
     * @return 房间对象，如果不存在则返回null
     */
    public GameRoom getRoomById(String roomId) {
        return roomIdToRoom.get(roomId);
    }

    // ==================== 游戏逻辑方法 ====================


    /**
     * 开始游戏
     * TODO: 练习二.1 - 实现 startGame 方法
     * 要求：
     * 1. 验证房间是否存在
     * 2. 验证是否为房间创建者
     * 3. 检查房间状态是否为 WAITING
     * 4. 检查房间人数是否至少2人
     * 5. 生成新的目标数字
     * 6. 设置房间状态为 PLAYING
     * 7. 重置所有玩家的猜测状态
     * 8. 返回 GameRoom 实体（包含敏感信息如 targetNumber）
     *
     * @param sessionId 创建者的会话ID
     * @param roomId 房间ID
     * @return 开始游戏结果（返回内部 GameRoom 实体）
     */
    public Result<GameRoomDto> startGame(String sessionId, String roomId) {
        // TODO: 实现开始游戏逻辑


        // 临时返回，避免编译错误
        return Result.failure("请实现 startGame 方法");
    }

    /**
     * 停止游戏
     * TODO: 练习二 - 实现 stopGame 方法
     * 要求：
     * 1. 验证房间是否存在
     * 2. 验证是否为房间创建者
     * 3. 检查房间状态是否为 PLAYING
     * 4. 设置房间状态为 WAITING
     * 5. 重置所有玩家的猜测状态
     * 6. 返回 GameRoom 实体（包含敏感信息如 targetNumber）
     *
     * @param sessionId 创建者的会话ID
     * @param roomId 房间ID
     * @return 停止游戏结果（返回内部 GameRoom 实体）
     */
    public Result<GameRoomDto> stopGame(String sessionId, String roomId) {
        // TODO: 实现停止游戏逻辑

        // 临时返回，避免编译错误
        return Result.failure("请实现 stopGame 方法");
    }

    /**
     * 玩家猜测数字
     * TODO: 练习二 - 实现 makeGuess 方法
     * 要求：
     * 1. 验证房间是否存在
     * 2. 检查房间状态是否为 PLAYING
     * 3. 验证玩家是否在房间中
     * 4. 检查玩家是否已经猜对（防止重复猜测）
     * 5. 验证猜测数字范围（1-100）
     * 6. 记录玩家猜测信息
     * 7. 判断猜测结果（与目标数字比较）
     * 8. 构建并返回 GuessResultDto 对象
     *
     * 注意：此方法直接返回 GuessResultDto，不是内部实体
     * 这与 startGame/stopGame 不同，它们返回 GameRoom 实体
     *
     * @param sessionId 玩家会话ID
     * @param roomId 房间ID
     * @param guess 猜测的数字
     * @return 猜测结果（直接返回 GuessResultDto，无需转换）
     */


    // ==================== 基础工具方法 ====================
    
    /**
     * 创建玩家对象
     * 
     * @param name 玩家名称
     * @param sessionId 会话ID
     * @return 玩家对象
     */
    private Player createPlayer(String name, String sessionId) {
        Player player = new Player();
        player.setName(name);
        player.setSessionId(sessionId);
        return player;
    }

    /**
     * 生成目标数字（1-100）
     * 
     * @return 1-100之间的随机数
     */
    private Integer generateTargetNumber() {
        return new Random().nextInt(100) + 1;
    }

    /**
     * 生成房间ID
     * 格式：room_时间戳_随机数
     * 
     * @return 唯一的房间ID
     */
    private String generateRoomId() {
        return "room_" + System.currentTimeMillis() + "_" + (int)(Math.random() * 1000);
    }
}

